using System;
using System.Collections.Generic;
using Microsoft.SharePoint;
using AymanElHattab.SharePoint.Logging;

namespace Simient.WSS3.Features.SecureTask
{
    public class SecureTaskItemReceiver : SPItemEventReceiver
    {
        //Keep this name in sync with SecureTaskItemReceiver
        private static readonly string configPropertyName = "#@:SecureTask:@#";

        /// <summary>
        /// Initializes a new instance of the Microsoft.SharePoint.SPItemEventReceiver class.
        /// </summary>
        public SecureTaskItemReceiver()
        {
        }

        /// <summary>
        /// Synchronous before event that occurs when an existing item is changed, for example, when the user changes data in one or more fields.
        /// </summary>
        /// <param name="properties">
        /// A Microsoft.SharePoint.SPItemEventProperties object that represents properties of the event handler.
        /// </param>
        public override void ItemUpdating(SPItemEventProperties properties)
        {
            SharePointLogger.LogStartOfMethod();
            bool updateTask = true;
            string errorMsg = "SecureTask feature is activated on this web. ";
            string afterStatus;
            try
            {
                // For OOB task content types.
                afterStatus = properties.AfterProperties["Status"].ToString();
            }
            catch
            {
                // For workflow tasks
                afterStatus = properties.AfterProperties["FormData"].ToString();
            }

            // If config not set and property is null, this returns zero
            int configOption = Convert.ToInt32(properties.ListItem.ParentList.RootFolder.Properties[configPropertyName]);

            using (SPSite site = new SPSite(properties.WebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    SPUser assignedTo = null;
                    if (properties.ListItem[SPBuiltInFieldId.AssignedTo] != null)
                    {
                        string assignedToUSerId = properties.ListItem[SPBuiltInFieldId.AssignedTo].ToString();
                        int assignedUserId = Convert.ToInt32(assignedToUSerId.Substring(0, assignedToUSerId.IndexOf(";#")));
                        assignedTo = web.AllUsers.GetByID(assignedUserId);
                    }
                    
                    SPUser currentUser = web.AllUsers.GetByID(properties.CurrentUserId);

                    switch (configOption)
                    {
                        // Secure only 'set status to completed' action from non-allowed users
                        case 0:
                            //If status is changed to Completed then check whether allowed person has updated or not
                            if (string.Compare(afterStatus,
                                properties.ListItem[SPBuiltInFieldId.TaskStatus].ToString(),
                                true) != 0)
                            {
                                if (string.Compare(afterStatus, "completed", true) == 0)
                                {
                                    if (!AllowedUsers(site, web, currentUser, assignedTo))
                                    {
                                        updateTask = false;
                                        errorMsg += "You are not allowed to set the task status as Completed. " + 
                                        "Only System account, Site Admins, Full Control users, or AssignedTo user can perform this action";
                                    }
                                }
                            }
                            break;
                        //Secure all actions from non-allowed users
                        case 1:
                            if (!AllowedUsers(site, web, currentUser, assignedTo))
                            {
                                updateTask = false;
                                errorMsg += "You are not allowed to perform any action on this task. " +
                                "Only System account, Site Admins, Full Control users, or AssignedTo user can edit this task.";
                            }
                            break;
                        default:
                            // Do not throw exception. Just log warning
                            SharePointLogger.LogWarning("Unexpected value for property " + configPropertyName);
                            break;
                    }                    

                    if (!updateTask)
                    {
                        properties.Cancel = true;
                        properties.ErrorMessage = errorMsg;
                        return;
                    }

                    base.ItemUpdating(properties);
                }
            }          

            SharePointLogger.LogEndOfMethod();
        }

        private bool AllowedUsers(SPSite site, SPWeb web, SPUser currentUser, SPUser assignedToUser)
        {
            //System account, Site Admin , Full Access and Assigned to user should be allowed to Edit the task
            if (site.SystemAccount.ID == currentUser.ID || web.CurrentUser.IsSiteAdmin)
            {
                return true;
            }

            if (assignedToUser != null && web.CurrentUser.ID == assignedToUser.ID)
            {
                return true;
            }
            
            // Get a reference the roles that are bound to the current user and the role 
            // definition to which we need to verify the user against 
            SPRoleDefinitionBindingCollection usersRoles = web.AllRolesForCurrentUser;
            SPRoleDefinitionCollection roleDefinitions = web.RoleDefinitions;
            SPRoleDefinition fullControlRoleDefinition = roleDefinitions["Full Control"];

            if (usersRoles.Contains(fullControlRoleDefinition))
            {
                return true;
            }            

            //execution reaches here then return false.
            return false;
        }
    }
}
